const { Parser, ParseOptions } = require("./pyright_internal/parser/parser");
const { DiagnosticSink } = require("./pyright_internal/common/diagnosticSink");
const { ParseTreeWalker } = require("./pyright_internal/analyzer/parseTreeWalker");
// const { NameNode } = require("./pyright_internal/parser/parseNodes");
const { AnalyzerService } = require("./pyright_internal/analyzer/service");
const { Program } = require("./pyright_internal/analyzer/program");
const { ConfigOptions } = require('./pyright_internal/common/configOptions');
const { createFromRealFileSystem } = require('./pyright_internal/common/realFileSystem');
const { FullAccessHost } = require('./pyright_internal/common/fullAccessHost');
const { ImportResolver } = require('./pyright_internal/analyzer/importResolver');
const { CancellationToken } = require('vscode-languageserver');
const { extractParameterDocumentation } = require('./pyright_internal/analyzer/docStringUtils');
// const fs = require('fs');

class treeWalker extends ParseTreeWalker {
    constructor(srcFile, program) {
        super();
        this._srcFile = srcFile;
        this._program = program;
    }

    visitName(node/* : NameNode */) {
        // console.log(node);
        // console.log(node.token.value);
        // console.log(this._srcFile);
        let pos = {line: 0, character: node.start+1};
        let hoverResult = this._program.getHoverForPosition(this._srcFile, pos, 'plaintext', CancellationToken.None);
        // console.log(hoverResult);
        let sigHelp = this._program.getSignatureHelpForPosition(this._srcFile, pos, 'plaintext', CancellationToken.None);
        // console.log(sigHelp);
        // console.log(this._program.getFileCount());
        if (hoverResult) {
            for (let part of hoverResult.parts) {
                if (!part.python) {
                    let m = part.text.match(/@英文\s(.+)/)/*  || part.text.match(/@中文\s(.+)/) */;
                    if (m) node.token.value = m[1].trim();
                    break;
                }
            }
        }

        if (sigHelp) {
            // console.log(sigHelp.signatures[0].documentation.value);
            // let doc = extractParameterDocumentation(sigHelp.signatures[0].documentation.value, node.token.value);
            // console.log(doc);
            let doc = sigHelp.signatures[0].documentation.value;
            let m = doc.match(eval('/@@ ' + node.token.value + ' @英文(.+)/'));
            // console.log(m);
            if (m) node.token.value = m[1].trim();
        }

        return true;
    }
}
const keywordTypeMap = {
    0: 'and',
    1: 'as',
    2: 'assert',
    3: 'async',
    4: 'await',
    5: 'break',
    6: 'case',
    7: 'class',
    8: 'continue',
    9: '__debug__',
    10: 'def',
    11: 'del',
    12: 'elif',
    13: 'else',
    14: 'except',
    15: 'False',
    16: 'finally',
    17: 'for',
    18: 'from',
    19: 'global',
    20: 'if',
    21: 'import',
    22: 'in',
    23: 'is',
    24: 'lambda',
    25: 'match',
    26: 'None',
    27: 'nonlocal',
    28: 'not',
    29: 'or',
    30: 'pass',
    31: 'raise',
    32: 'return',
    33: 'True',
    34: 'try',
    35: 'while',
    36: 'with',
    37: 'yield',
    38: 'is not',
    39: 'not in'
}

const operatorTypeMap = {
    0: '+',
    1: '+=',
    2: '=',
    3: '&',
    4: '&=',
    5: '~',
    6: '|',
    7: '|=',
    8: '^',
    9: '^=',
    10: '/',
    11: '/=',
    12: '==',
    13: '//',
    14: '//=',
    15: '>',
    16: '>=',
    17: '<<',
    18: '<<=',
    19: '<>',
    20: '<',
    21: '<=',
    22: '@',
    23: '@=',
    24: '%',
    25: '%=',
    26: '*',
    27: '*=',
    28: '!=',
    29: '**',
    30: '**=',
    31: '>>',
    32: '>>=',
    33: '-',
    34: '-=',
    35: ':=',
    36: 'and',
    37: 'or',
    38: 'not',
    39: 'is',
    40: 'is not',
    41: 'in',
    42: 'not in',
}

function tokensToString(tokens) {
    let transList = [];
    let startAndLen = 0;
    let newLn = false;
    let indent = 0;

    function pushVal(token, val) {
        if (newLn) {
            // let 整数部分 = parseInt((token.start - startAndLen) / (indent + 2));
            // let 余数部分 = (token.start - startAndLen) % (indent + 2);
            // transList.push('\n'.repeat(整数部分) + ' '.repeat(余数部分) + val);
            transList.push(' '.repeat(indent) + val);
        } else {
            transList.push(' '.repeat(token.start - startAndLen) + val);
        }

        startAndLen = token.start + token.length;
        newLn = false;
    }

    for (let token of tokens) {
        switch (token.type) {
            case 2:
                if (token.newLineType == 2) { // 其他类型待处理
                    transList.push('\n');
                    startAndLen = token.start + token.length;
                    newLn = true;
                }
                break;

            case 3:
                indent = token.indentAmount;
                pushVal(token, '');
                break;

            case 4:
                indent = token.indentAmount;
                if (indent === 0) {
                    pushVal(token, '\n');
                } /* else {
                    newLn = true;
                    pushVal(token, '');
                } */
                break;

            case 5:
                let flag = token.flags;

                let prefix = '';
                prefix = flag & 16 ? prefix + 'u' : prefix;
                prefix = flag & 8 ? prefix + 'r' : prefix;
                prefix = flag & 32 ? prefix + 'b' : prefix;
                prefix = flag & 64 ? prefix + 'f' : prefix;

                let quote;
                if (flag & 1) {
                    quote = flag & 4 ? "'''" : "'";
                } else {
                    quote = flag & 4 ? '"""' : '"';
                }

                pushVal(token, prefix + quote + token.escapedValue + quote);
                break;

            case 6: // 不能还原 0b/0o/0x... 这些值
                let v;
                if (token.isImaginary) {
                    v = token.value + 'j';
                } else {
                    v = token.value;
                }

                pushVal(token, v);
                break;

            case 7:
                // console.log(token.value);
                pushVal(token, token.value);
                break;

            case 8:
                pushVal(token, keywordTypeMap[token.keywordType]);
                break;

            case 9:
                pushVal(token, operatorTypeMap[token.operatorType]);
                break;

            case 10:
                pushVal(token, ':');
                break;

            case 11:
                pushVal(token, ';');
                break;

            case 12:
                pushVal(token, ',');
                break;

            case 13:
                pushVal(token, '(');
                break;

            case 14:
                pushVal(token, ')');
                break;

            case 15:
                pushVal(token, '[');
                break;

            case 16:
                pushVal(token, ']');
                break;

            case 17:
                pushVal(token, '{');
                break;

            case 18:
                pushVal(token, '}');
                break;

            case 19:
                pushVal(token, '...');
                break;

            case 20:
                pushVal(token, '.');
                break;

            case 21:
                pushVal(token, '->');
                break;

            case 22:
                pushVal(token, '`');
                break;

            default:
                break;
        }
    }
    // console.log(transList);
    // 针对 mpy, 将其中的汉字转成 utf 编码; 若是 python, 则直接 return transList.join('')
    let arr = transList.join('').split('');
    for (let c in arr) {
        if (/[\u4e00-\u9fa5]/.test(arr[c])) arr[c] = 'u' +  arr[c].charCodeAt(0).toString(16);
    }
    return arr.join('');
}

function transpile(eCode, sourceFile, dir) {
    const parser = new Parser();
    let result = parser.parseSourceFile(eCode, new ParseOptions(), new DiagnosticSink())
    // console.log(result);

    const configOptions = new ConfigOptions(dir, 'off');
    // configOptions.internalTestMode = true;
    configOptions.pythonPath = 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python39\\python.exe';
    // configOptions.typeshedPath = 'G:\\goldenpy\\src\\typeshed-fallback';
    configOptions.stubPath = 'G:\\goldenpy\\src\\mpy_data';
    configOptions.verboseOutput = true;
    // configOptions.useLibraryCodeForTypes = true;
    const fs = createFromRealFileSystem();
    const importResolver = new ImportResolver(fs, configOptions, new FullAccessHost(fs));
    const program = new Program(importResolver, configOptions);
    program.setTrackedFiles([sourceFile/* , 'g:/frontend/gold.py' */]);

    // while (program.analyze()) {
    //     // Continue to call analyze until it completes. Since we're not
    //     // specifying a timeout, it should complete the first time.
    // }
    program.printDependencies('.', true);

    const walker = new treeWalker(sourceFile, program);
    walker.walk(result.parseTree);
    // console.log(result.tokenizerOutput.tokens._items);
    return tokensToString(result.tokenizerOutput.tokens._items);
}


module.exports = {
    transpile
}